﻿/*
 * Authors:
 *   钟峰(Popeye Zhong) <9555843@qq.com>
 *
 * Copyright (c) 2017 Flagwind Game Studio. All rights reserved.
 */

using System;

namespace Flagwind.Gaming
{
	/// <summary>
	/// 表示扑克牌的结构。
	/// </summary>
	public struct Poker
	{
		#region 私有常量
		private const int DECK_COUNT = 54; //一副扑克牌的张数(SUIT_COUNT*4+2)
		private const int SUIT_COUNT = 13; //一套花色牌的张数(2~10,J,Q,K,A)

		private const byte RED_JOKER_VALUE = 53;
		private const byte BLACK_JOKER_VALUE = 52;
		#endregion

		#region 成员字段
		private byte _value;
		private byte _rank;
		private PokerSuit? _suit;
		#endregion

		#region 构造函数
		public Poker(byte value)
		{
			if(value >= DECK_COUNT)
				throw new ArgumentOutOfRangeException(nameof(value));

			_value = value;
			_rank = GetRank(_value);
			_suit = GetSuit(_value);
		}

		public Poker(byte value, byte rank, PokerSuit? suit = null)
		{
			if(value >= DECK_COUNT)
				throw new ArgumentOutOfRangeException(nameof(value));

			_value = value;
			_rank = rank;
			_suit = suit ?? GetSuit(_value);
		}
		#endregion

		#region 公共属性
		/// <summary>
		/// 获取扑克牌的值，取值范围为：0~53。
		/// </summary>
		public byte Value
		{
			get
			{
				return _value;
			}
		}

		/// <summary>
		/// 获取扑克牌的点数（即花色索引值），具体请参考备注说明。
		/// </summary>
		/// <remarks>
		///		<para>如果为鬼牌（大小王），则该属性值为零；</para>
		///		<para>如果不为空鬼牌（大小王），则该属性值范围为1~13，分别表示：Ace、2、3、4、5、6、7、8、9、10、Jack、Queen、King。</para>
		/// </remarks>
		public byte Rank
		{
			get
			{
				return _rank;
			}
		}

		/// <summary>
		/// 获取扑克牌的花色，如果为空则表示为大小王。
		/// </summary>
		public PokerSuit? Suit
		{
			get
			{
				return _suit;
			}
		}
		#endregion

		#region 符号重写
		public static implicit operator Poker(byte value)
		{
			if(value >= DECK_COUNT)
				throw new ArgumentOutOfRangeException(nameof(value));

			return new Poker(value);
		}

		public static explicit operator byte(Poker poker)
		{
			return poker._value;
		}

		public static bool operator == (Poker a, Poker b)
		{
			return a._value == b._value;
		}

		public static bool operator != (Poker a, Poker b)
		{
			return a._value != b._value;
		}
		#endregion

		#region 重写方法
		public override bool Equals(object obj)
		{
			if(obj == null || obj.GetType() != typeof(Poker))
				return false;

			return ((Poker)obj)._value == _value;
		}

		public override int GetHashCode()
		{
			return _value;
		}

		public override string ToString()
		{
			if(this.Suit == null)
				return _value == BLACK_JOKER_VALUE ? "Joker(Black)" : "Joker(Red)";

			var color = '\0';

			switch(this.Suit.Value)
			{
				case PokerSuit.Heart:
					color = '\u2665';
					break;
				case PokerSuit.Spade:
					color = '\u2660';
					break;
				case PokerSuit.Diamond:
					color = '\u2666';
					break;
				case PokerSuit.Club:
					color = '\u2663';
					break;
			}

			string label;

			switch(_value % SUIT_COUNT)
			{
				case 0:
					label = "A";
					break;
				case 10:
					label = "J";
					break;
				case 11:
					label = "Q";
					break;
				case 12:
					label = "K";
					break;
				default:
					label = ((_value % SUIT_COUNT) + 1).ToString();
					break;
			}

			return $"{color} {label}";
		}
		#endregion

		#region 公共方法
		public Poker Clone()
		{
			var poker = new Poker();

			poker._value = _value;
			poker._rank = _rank;
			poker._suit = _suit;

			return poker;
		}
		#endregion

		#region 静态方法
		/// <summary>
		/// 获取指定牌值对应的点数。
		/// </summary>
		/// <param name="value">指定的牌值，有效值为0~53。</param>
		/// <returns>返回指定牌值的点数，如果牌值是无效值或鬼牌则返回零，否则返回对应的点数。</returns>
		public static byte GetRank(byte value)
		{
			if(value < DECK_COUNT - 2)
				return (byte)(value % SUIT_COUNT + 1);

			return 0;
		}

		/// <summary>
		/// 获取指定牌值对应的花色。
		/// </summary>
		/// <param name="value">指定的牌值，有效值为0~53。</param>
		/// <returns>返回指定牌值的点数，如果牌值是无效值或鬼牌则返回空，否则返回对应的花色枚举。</returns>
		public static PokerSuit? GetSuit(byte value)
		{
			if(value < DECK_COUNT - 2)
				return (PokerSuit)Math.Floor((double)value / SUIT_COUNT);

			return null;
		}

		/// <summary>
		/// 判断牌值是否为鬼牌（即大小王）。
		/// </summary>
		/// <param name="value">指定的牌值，有效值为0~53。</param>
		/// <returns>如果指定牌值是鬼牌则返回真(True)，否则返回假(False)。</returns>
		public static bool IsJoker(byte value)
		{
			return value == RED_JOKER_VALUE || value == BLACK_JOKER_VALUE;
		}

		/// <summary>
		/// 判断指定牌值是否有效。
		/// </summary>
		/// <param name="value">指定的牌值，有效值为0~53。</param>
		/// <returns>如果牌值小于54则返回真(True)，否则返回假(False)。</returns>
		public static bool IsValid(byte value)
		{
			return value < DECK_COUNT;
		}
		#endregion

		#region 嵌套子类
		/// <summary>
		/// 表示大小王的命名类。
		/// </summary>
		public static class Joker
		{
			/// <summary>
			/// 表示大王（大鬼）的扑克牌。
			/// </summary>
			public static readonly Poker Red = new Poker(RED_JOKER_VALUE);

			/// <summary>
			/// 表示小王（小鬼）的扑克牌。
			/// </summary>
			public static readonly Poker Black = new Poker(BLACK_JOKER_VALUE);
		}
		#endregion
	}
}
